package sock

import (
	"errors"
	"io"
	"net"
	"strconv"
	"strings"
	"time"
)

// 如果地址是个端口号，自动在端口号前加冒号
/*
* addr: 地址, 格式为: ip:port 或 :port 或 port
 */
func FillAddr(addr string) string {
	addr = strings.TrimSpace(addr)
	if addr == "" {
		return addr
	}
	if strings.Contains(addr, ":") {
		return addr
	}
	_, err := strconv.Atoi(addr)
	if err != nil {
		return addr
	}
	return ":" + addr
}

// CreateTcpListener 创建TCP监听器
/*
* host: 主机地址, 格式为: ip:port 或 :port 或 port
 */
func CreateTcpListener(host string) (listener *net.TCPListener, err error) {
	host = FillAddr(host)

	tcpAddr, err := net.ResolveTCPAddr("tcp", host)
	if err != nil {
		return nil, err
	}

	listener, err = net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return nil, err
	}
	return
}

// CreateTcpConnect 创建TCP连接
/*
* host: 主机地址, 格式为: ip:port
* timeout: 超时时间, 单位为毫秒, 默认为30000毫秒
 */
func CreateTcpConnect(host string, timeout ...int64) (conn *net.TCPConn, err error) {
	timeoutms := int64(30000)
	if len(timeout) > 0 {
		timeoutms = min(max(timeout[0], 1000), 120000)
	}

	//conn, err = net.DialTCP("tcp", nil, tcpAddr)
	c, err := net.DialTimeout("tcp", host, time.Millisecond*time.Duration(timeoutms))
	if err != nil {
		return nil, err
	}
	conn, ok := c.(*net.TCPConn)
	if !ok {
		err = errors.New("not tcp connect")
		return
	}

	//设置tcp心跳时间
	conn.SetKeepAlive(true)
	conn.SetKeepAlivePeriod(60 * time.Second)
	return
}

// CreateUdpConnect 创建UDP连接
/*
* address: 主机地址, 格式为: ip:port
 */
func CreateUdpConnect(address string) (conn *net.UDPConn, err error) {
	address = FillAddr(address)

	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, err
	}
	conn, err = net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil, err
	}
	return conn, err
}

// CreateUdpListener 创建UDP监听器
/*
* host: 主机地址, 格式为: ip:port 或 :port 或 port
 */
func CreateUdpListener(host string) (listener *net.UDPConn, err error) {
	udpAddr, err := net.ResolveUDPAddr("udp", host)
	if err != nil {
		return nil, err
	}

	listener, err = net.ListenUDP("udp", udpAddr)
	if err != nil {
		return nil, err
	}
	return
}

// 再指定的时间读取指定的字节数, 当没有错误发生时, num = len(data)
func ReadTimeoutMs(conn net.Conn, data []byte, mstimeout int) (num int, err error) {
	length := len(data)
	num = 0
	mstimeout = min(max(mstimeout, 50), 1800000)
	err = conn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(mstimeout)))
	if err != nil {
		return
	}
	for num < length {
		n, err := conn.Read(data[num:]) ///*read(tcpconn, data[num:])*/
		if err != nil {
			return num, err
		}
		if n > 0 {
			conn.SetReadDeadline(time.Now().Add(time.Millisecond * time.Duration(mstimeout)))
			num += n
		}
	}

	return
}

//ReadTimeout ...
/*在指定的时间内,接收指定的字节数, 当没有错误发生时, num = len(data)
* 当有错误发生是num为实际接收的字节数
 */
func ReadTimeout(conn io.Reader, data []byte, mstimeout int) (num int, err error) {
	length := len(data)
	num = 0
	mstimeout = min(max(mstimeout, 20), 3600000)
	if tcpconn, ok := conn.(net.Conn); ok {
		return ReadTimeoutMs(tcpconn, data, mstimeout)
	} else {
		n := 0
		t := time.Now()
		for num < length && mstimeout > 0 {
			//log.Println(num, length)
			n, err = conn.Read(data[num:])
			//if err != nil && err.Error() == "EOF" {
			if err != nil {
				return num, err
			}
			//log.Println(err, err.Error() != "EOF")
			num += n
			if n == 0 {
				t1 := int(time.Since(t) / time.Millisecond)
				if t1 >= mstimeout {
					err = errors.New("timeout")
					break
				} else if t1+50 > mstimeout {
					time.Sleep(time.Duration(mstimeout-t1) * time.Millisecond)
				} else {
					time.Sleep(time.Millisecond * 50)
				}
				continue
			}
		}
	}
	return
}

//Read ...
/*接收指定的字节数, 当没有错误发生时, num = len(data)
* 当有错误发生是num为实际接收的字节数
 */
func Read(conn io.Reader, data []byte) (num int, err error) {
	length := len(data)
	num = 0
	for num < length {
		n, err := conn.Read(data[num:])
		if err != nil {
			return num, err
		}
		num += n
	}
	return
}

// ReadLenString 读取指定长度的字符串
/*
* 格式: 2字节长度 + 数据
 */
func ReadLenString(conn net.Conn, mstimeout int) (str string, err error) {
	data, err := ReadLenBytes(conn, mstimeout)
	str = string(data)
	return
}

// ReadLenBytes 读取指定长度的字节
/*
* 格式: 2字节长度 + 数据
 */
func ReadLenBytes(conn net.Conn,  mstimeout int) (data []byte, err error) {
	data = make([]byte, 2)
	n, err := ReadTimeoutMs(conn, data, mstimeout)
	if err != nil {
		n = 0
		return
	}
	_ = n
	l := uint16(data[0])<<8 | uint16(data[1])
	data = make([]byte, l)
	n, err = ReadTimeoutMs(conn, data, mstimeout)
	if err != nil {
		return data[:n], err
	}
	return
}

// WriteLenBytes 写入指定长度的字节
/*
* 格式: 2字节长度 + 数据
 */
func WriteLenBytes(conn io.Writer, data []byte) (n int, err error) {
	b := make([]byte, 2)
	l := len(data)
	b[0] = byte(uint16(l) >> 8)
	b[1] = byte(l)
	_, err = conn.Write(b)
	if err != nil {
		n = 0
		return
	}
	n, err = conn.Write(data)
	return
}

// WriteLenString 写入指定长度的字符串
/*
* 格式: 2字节长度 + 数据
 */
func WriteLenString(conn io.Writer, str string) (n int, err error) {
	return WriteLenBytes(conn, []byte(str))
}
